		
	var Terminal = Class.create({
		options:{
			past:'past',
			present:'present',
			question:'question',
			future:'future'
		},
		cic:-1,
		SJ:false,
		initialize:function(options){
			Object.extend(this.options, options || {});
			this.sesh = new Terminal.Session('<?=usr()->name?>', '<?=sm_getcwd()?>');
			Event.observe(this.$('future'), 'keydown', function(e){
				switch(e.keyCode){
					case (Event.KEY_RETURN):
						return this.readInput();
					case (Event.KEY_UP):
						return this.arrowCom(1);
					case (Event.KEY_DOWN):
						return this.arrowCom();
				}
			}.bind(this));
			Event.observe(document, 'keydown', function(e){
				if(!this.SJ || !e.ctrlKey || (e.keyCode != 67)) return;
				this.SJ.cancel(e);
			}.bind(this));
			this.ready();
		},
		arrowCom:function(up){
			var l = this.sesh.index.length;
			if(this.cic == -1) this.cic = l;
			if(up){
				if(this.cic) this.cic--;
			}else{
				this.cic++;
				if(this.cic == l) return this.$('future').value = '';
			}			
			var k = this.sesh.index[this.cic]
			this.$('future').value = this.sesh.history[k].params.com;
		},
		$:function(e){
			return $(this.options[e]);	
		},
		ready:function(){
			this.SJ = false;
			this.$('past').innerHTML = (this.$('past').innerHTML + '\n' + this.$('present').innerHTML).trim();
			this.$('present').innerHTML = '';
			this.$('present').hide();
			this.promptUser();
			this.cic = this.sesh.index.length;
		},
		oriStr:function(){
			var a = this.sesh.dir.split('::');
			return this.sesh.usr+'@'+a[0]+' ['+a[1]+']# ';
		},
		hideInp:function(){
			this.$('present').show();
			this.$('future').hide();
			this.$('question').hide();
		},
		readInput:function(){
			var inp = this.$('future').value.trim();
			this.$('present').innerHTML += this.$('question').innerHTML + (this.$('question').hash ? '' : inp) + '\n';
			var params = {};
			if(this.SJ && this.SJ.getHeader('exchange') == 'interactive'){
				if(!inp) return;
				this.hideInp();
				if(this.$('question').hash){
					params.reply = (this.$('question').salt) ? hex_sha1(hex_sha1(inp)+this.$('question').salt) : hex_sha1(inp);	
				}else params.reply = inp;
				this.SJ.reply(params);
				return;
			}
			if(!inp) return this.ready();
			this.hideInp();
			params.com = inp;
			this.SJ = this.sesh.send(params, this);
		},
		update:function(b, p){
			if(this.SJ.getHeader('exchange') == 'interactive'){
				if(b) this.$('present').innerHTML += b;
				if(p) this.promptUser(p);
			}else this.$('present').innerHTML = this.$('question').innerHTML + this.$('future').value + '\n' + b;
		},
		promptUser:function(o){
			if(!o) o = {}
			this.$('present').innerHTML = (this.$('present').innerHTML.trim() + '\n').ltrim();
			this.$('question').hash = o.h ? o.h : false;
			this.$('question').salt = o.s ? o.s : false;
			this.$('question').innerHTML = o.p ? o.p + ': ' : this.oriStr();
			this.$('question').style.width = this.$('question').innerHTML.length/1.67 + 'em';
			this.$('question').show();
			this.$('future').type = o.h ? 'password' : 'text';
			this.$('future').value = '';
			this.$('future').show();
			this.$('future').focus();
			// stupid safari bug to fix focus
			this.$('future').value += '';
		},
		respondToReadyState:function(SJ){
			switch(SJ.readyState){
				case 'Interactive':
					this.update(SJ.body, SJ.prompt);
					break;
				case 'Complete':
					this.update(SJ.body)
					this.ready();
			}
		}
	});
	
	Terminal.Session = Class.create({
		index:[],
		history:{},
		initialize:function(usr, dir){
			this.usr = usr;
			this.dir = dir;
		},
		send:function(params, caller){
			params.dir = this.dir;
			var SJ = new Smashjax(params, caller, this);
			this.index.push(SJ.id);
			return this.history[SJ.id] = SJ;
		},
		respondToReadyState:function(SJ){
			switch(SJ.readyState){
				case 'Complete':
					['dir', 'usr'].each(function(k){ if(SJ.getHeader(k)) this[k] = SJ.getHeader(k); }.bind(this))
			}
		}
	});
	
	var Smashjax = Class.create({
		body:'',
		prompt:false,
		headers:{},
		initialize:function(params, caller, session){
			this.params = params;
			this.caller = caller;
			this.session = session;
			params.id = this.id = new Date().getTime();
			var ops = {
				onLoaded:function(t){
					this.getReadyState(t);
					this.getStatus();
					this.getHeaders();
					this.sendReadyState();
				}.bind(this)	
			};
			['Create', 'Interactive', 'Success', 'Failure', 'Complete'].each(function(F){
				ops['on'+F] = this.ajaxListener.bind(this); 
			}.bind(this));
			this.send(params, ops);
		},
		ajaxListener:function(t){
			this.getReadyState(t);
			this.getBody();
			this.sendReadyState();
		},
		send:function(params, options){
			if(!options) options = {};
			Object.extend(options, {
				method:'post',
				parameters:params,
				requestHeaders:{'Smajax':'Yes, please'}
			});
			return new Ajax.Request('/@smashjax', options);
		},
		getReadyState:function(t){
			this.transport = t;
			this.readyState = Ajax.Request.Events[t.readyState];
		},
		sendReadyState:function(){
			[this.session, this.caller].each(function(o){
				if(o && Object.isFunction(o.respondToReadyState)) o.respondToReadyState(this);				  
			}.bind(this));
		},
		getStatus:function(){
			this.status = this.transport.getStatus();
		},
		getHeaders:function(){
			this.headers.responses = 0;
			var h = this.transport.getAllHeaders();
			if(is_null(h)) return;
			h.split('\n').each(function(l){
				var i = l.indexOf(': ');
				this.headers[l.substr(0, i)] = l.substr(i+2);
			}.bind(this));
		},
		getBody:function(){
			var s = this.transport.responseText;
			this.body = '';
			if(this.getHeader('exchange') !== 'interactive') return this.body = s;
			var jb = '<![JBRK]!>';
			if(s.substr(s.length-10) != jb) return '';
			var a = s.substr(0, s.length-10).split(jb);
			if(this.headers.responses == a.length) return '';
			s = '';
			while(this.headers.responses < a.length){
				var o = a[this.headers.responses].evalJSON();
				this.headers.responses++;
				s += o.m ? o.m : '';
			}
			this.prompt = o.p ? o : false;
			return this.body = s;		
		},
		getHeader:function(name){
			return this.transport.getHeader(name);
		},
		reply:function(params, options){
			params.com = 'interact '+this.id;
			return this.send(params, options);
		},
		cancel:function(e){
			if(this.readyState == 'Complete') return;
			if(e) Event.stop(e);
			return this.send({com:'stop '+this.id});
		}
	});
	
	window.queue(function(){
		var T = new Terminal();
	});
	
